#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <list>
#include <sstream>
#include <string>
#include <vector>
#include "../proto/database.pb.h"
#include <google/protobuf/message.h>
#include <google/protobuf/descriptor.h>
#include <boost/program_options.hpp>
#include <boost/asio.hpp>

using namespace boost;
using boost::asio::ip::tcp;
namespace po = boost::program_options;

#include "bufferDB.h"
#include "bufDBClient.h"


using namespace std;

void enterPlayerInfo(db::Database* database);
int menu();
void retrieveMenu(bufDBClient *);
void StoreData(string, bufDBClient);


int main(int argc, char* argv[])
{
   GOOGLE_PROTOBUF_VERIFY_VERSION;

   bufDBClient client;	

   string data;
   string receivedData;
   string filename;
   int choice;
   bool loop = true;

   po::options_description desc("Allowed options");
   desc.add_options()
      ("help,h", "produce help message")
      ("write,w", po::value<string>(), "write file")
      ("host", po::value<string>(), "host name")
      ;

   po::positional_options_description pod;
   pod.add("host", -1);

   po::variables_map vm;
   //po::store(po::parse_command_line(argc, argv, desc), vm);
   po::store(po::command_line_parser(argc, argv).options(desc).positional(pod).run(), vm);
   po::notify(vm);

   if (vm.count("help")) {
      cout << desc << endl;
      exit(1);
   }

   if (vm.count("host") == 0)
   {
      cout << desc << endl;
      exit(1);
   }

   char * hostname = (char *) vm["host"].as<string>().c_str();		
   if (vm.count("write")) {
      //cin.ignore();
      StoreData(vm["write"].as<string>(), client);
      client.setWrite(true);
      client.serializeAndSend(hostname);
   } else {
      client.setWrite(false);
      client.serializeAndListen(hostname);
      retrieveMenu(&client);
   }

   // Optional:  Delete all global objects allocated by libprotobuf.
   google::protobuf::ShutdownProtobufLibrary();
}


void retrieveMenu(bufDBClient *client)
{
   boost::asio::io_service io_service;

   tcp::endpoint endpoint(tcp::v4(), 13);
   tcp::acceptor acceptor(io_service, endpoint);

   tcp::iostream mystream;
   cerr << "accepting..." << endl;
   acceptor.accept(*mystream.rdbuf());

   try
   {
      for (;;)
      {
         // parse the message into the class
         db::Database *msg = new db::Database();
         //if (!msg->ParseFromIstream(client->thestream)) {
         if (!msg->ParseFromIstream(&mystream)) {
            std::cerr << "Failed to parse " __FILE__ << " " << __LINE__ << std::endl;
            exit(1);
         }
         msg->PrintDebugString();
         break;
      }
   }
   catch (std::exception& e)
   {
      std::cerr << e.what() << std::endl;
   }
}


void StoreData(string filename, bufDBClient client)
{
   int msgSize[100];
   int tokenTotalCnt=0;

   //First Determine how many messages are in the file
   //The deque inData contains the data read from the file.
   //The array msgSize contains the size of each of the messages
   //ie: The number of tokens in each
   int totalInputMsgs = client.messagesInFile(filename, msgSize);

   int totalProtoMsgs = client.findRepeatedFields();

   int totalNestedTypes = client.findNestedTypes();


   if((totalInputMsgs != totalProtoMsgs) || (totalProtoMsgs != totalNestedTypes))
   {
      cout<<"ERROR NUMBER OF MSGS DOES NOT MATCH!!"<<endl;
      cout<<"Input Messages"<<totalInputMsgs<<endl;
      cout<<"Proto Messages"<<totalProtoMsgs<<endl;
      cout<<"Nested Types "<<totalNestedTypes<<endl;		
   }

   //Iterate through each message and store the fields
   //into seperate tokens.
   int tokenEnd = 0;
   for(int msgCnt = 0; msgCnt < totalInputMsgs; msgCnt++)
   {
      //This will split each message of the file into
      //tokens. One token for each comma seperated value.
      client.createFieldTokens(msgSize[msgCnt]);

      //Set your indexes to point to the correct field
      //Repeated field gives you the data
      //Nested field gives you the type of data
      client.setRepeatedMsgPtr(msgCnt);
      client.setNestedTypePtr(msgCnt);

      //Once you are pointing at the correct Nested Type
      //you must determine the type of each token in the Nested Type
      //This is equivalent to the type of each comma seperated value
      //on a line of the input file.
      int totalNestedTokenTypes = client.findNestedTokenTypes();		
      int tokenTypeCnt = 0;

      //Create a the first message element to set the tokens in.
      //Each new line of the input file needs to have an element
      //created in the proto buffer.
      //Thus subsequent elements will be created within the for loop.
      client.AddNestedMsgElement();			
      for(int tokenCnt = 0; tokenCnt<msgSize[msgCnt]*totalNestedTokenTypes; tokenCnt++)
      {
         client.setNestedTokenTypePtr(tokenTypeCnt);

         client.setTokensInProto(tokenTotalCnt);

         if(tokenTypeCnt<totalNestedTokenTypes-1)
         {
            tokenTypeCnt++;
         }
         else if(tokenCnt<msgSize[msgCnt]*totalNestedTokenTypes-1)
         {				
            tokenTypeCnt = 0;				
            client.AddNestedMsgElement();					
         }
         else
         {
            tokenTypeCnt = 0;
         }

         tokenTotalCnt++;
      }
   }

   //Test to see how the get function works.
   /*       client.setRepeatedMsgPtr(0);
            client.setNestedTypePtr(0);
            client.setNestedTokenTypePtr(0);
            client.setMessageEntryPtr(0);
            client.getString();*/



}		

